
#import "../Stone3DAPI/Stone3D.h"

#import "PatchMesh.h"
#import "../ControlPoint.bproj/ControlPoint.h"
#import "GetUVValue.h"
#import <ri/ri.h>

#define BEZIER		0
#define HERMITE 	1
#define BSPLINE		2
#define CATMULLROM	3


@implementation PatchMesh

- init
{
    id		temp, lastShape;
    int		u,v;
    float	incU, incV;
    float	tempV;
 
    nu = 4; nv = 4;
    if (![[GetUVValue instance] runModalForControl:&nu :&nv])
	return nil;
	
    [super init];
    
    NX_ZONEMALLOC([self zone], thePoints, RtPoint, (nu*nv));
        
    // Create and link control point objects
    controlPts = [[List allocFromZone:[self zone]] initCount:16];
    incU = 2.0 / (nu - 1);
    incV = 2.0 / (nv - 1);
    temp = nil;
    for (v=0; v<nv; v++) {
	tempV = -1.0 + v*incV;
	for (u=0; u<nu; u++) {
	    lastShape = temp;
	    temp = [[ControlPoint allocFromZone:[self zone]] init];
	    [temp translateTo:(-1.0 + u*incU) :tempV :0.0];
	    if (v || u) {
		[lastShape linkPeer:temp];
		[controlPts addObject:temp];
	    } else {	    
		[temp linkAncestor:self];
		[self linkDescendant:temp];
		[controlPts addObject:temp];
	    }
	}
    }

    uBasis = BEZIER; bcopy(&RiBezierBasis,&theUBasis,sizeof(RtBasis));
    vBasis = BEZIER; bcopy(&RiBezierBasis,&theVBasis,sizeof(RtBasis));
    uStep = 3;
    vStep = 3;
    showCtlPoints = YES;
    showHull = NO;
    
    // The X min and max
    boundingBox[0] = 999;  boundingBox[1] = -999;
    
    // The Y min and max
    boundingBox[2] = 999;  boundingBox[3] = -999;
    
    // The Z min and max
    boundingBox[4] = 999;  boundingBox[5] = -999;
    
    return self;
}

- (BOOL) hullVisible
{
    return showHull;
}

- setHullVisible:(BOOL) flag
{
    showHull = flag;
    return self;
}

- (BOOL)controlVisible
{
    return showCtlPoints;
}

- setControlVisible:(BOOL)flag
{
    int		i, max;
    id   	*clist;
    
    showCtlPoints = flag;
    for (i=0, max = [controlPts count],
	clist=NX_ADDRESS(controlPts);i<max;i++) {
	    [clist[i] setVisible:flag];
	}
    return self;
}

- (int)uBasis
{
    return uBasis;
}

- setUBasis:(int)theU
{
    uBasis = theU;
    switch (uBasis) {
	case BEZIER: bcopy(&RiBezierBasis,&theUBasis,sizeof(RtBasis));
		     uStep = RI_BEZIERSTEP;
		     break;
	case HERMITE: bcopy(&RiHermiteBasis,&theUBasis,sizeof(RtBasis));
		     uStep = RI_HERMITESTEP;
		     break;
	case BSPLINE: bcopy(&RiBSplineBasis,&theUBasis,sizeof(RtBasis));
		     uStep = RI_BSPLINESTEP;
		     break;
	case CATMULLROM: bcopy(&RiCatmullRomBasis,&theUBasis,sizeof(RtBasis));
		     uStep = RI_CATMULLROMSTEP;
		     break;
	default: bcopy(&RiBezierBasis,&theUBasis,sizeof(RtBasis));
    }
    return self;
}

- (int)vBasis 
{
    return vBasis;
}

- setVBasis:(int)theV
{
    vBasis = theV;
    switch (vBasis) {
	case BEZIER: bcopy(&RiBezierBasis,&theVBasis,sizeof(RtBasis));
		     vStep = RI_BEZIERSTEP;
		     break;
	case HERMITE: bcopy(&RiHermiteBasis,&theVBasis,sizeof(RtBasis));
		     vStep = RI_HERMITESTEP;
		     break;
	case BSPLINE: bcopy(&RiBSplineBasis,&theVBasis,sizeof(RtBasis));
		     vStep = RI_BSPLINESTEP;
		     break;
	case CATMULLROM: bcopy(&RiCatmullRomBasis,&theVBasis,sizeof(RtBasis));
		     vStep = RI_CATMULLROMSTEP;
		     break;
	default: bcopy(&RiBezierBasis,&theVBasis,sizeof(RtBasis));
    }
    return self;
}

// In order to provide selection capablity we must override Shapes instance 
// method calcBoundingBox.
- calcBoundingBox
{
    int		i, max;		// index variables
    id   	*clist;		// Address of control point list
    RtFloat 	pt[3];
    
    for (i=0, max = [controlPts count],
	clist=NX_ADDRESS(controlPts);i<max;i++) {
	    [clist[i] trans:&pt[0]];
	    if (pt[0] < boundingBox[0]) boundingBox[0] = pt[0];
	    if (pt[0] > boundingBox[1]) boundingBox[1] = pt[0];
	    if (pt[1] < boundingBox[2]) boundingBox[2] = pt[1];
	    if (pt[1] > boundingBox[3]) boundingBox[3] = pt[1];
	    if (pt[2] < boundingBox[4]) boundingBox[4] = pt[2];
	    if (pt[2] > boundingBox[5]) boundingBox[5] = pt[2];
    }
    return self;
}

- doRenderSelf: (Camera *) camera
{
    int		i, max;
    id   	*clist;
    
    [super doRenderSelf:camera]; 
    for (i=0, max = [controlPts count],
	clist=NX_ADDRESS(controlPts);i<max;i++) {
	    [clist[i] trans:&thePoints[i][0]];
	}
    RiBasis(theUBasis, (RtInt)uStep, theVBasis, (RtInt)vStep);
    RiPatchMesh(RI_BICUBIC,(RtInt)nu,RI_NONPERIODIC,(RtInt)nv,RI_NONPERIODIC,
		RI_P,(RtPointer)thePoints,RI_NULL);
    
    return self;
}
@end
	